﻿using log4net;
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.ComponentModel;
using System.Xml.Linq;
using System.Threading;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;

namespace Klinkby.MicroBus
{
    // HACK
    // TODO
    // netsh http add iplisten ipaddress=0.0.0.0:666

    //[ServiceContract]
    //public interface IReceiver
    //{
    //    [OperationContract()] // Action="*" ?!? // AsyncPattern = true
    //    void Publish(IMessage message);
    //}

    /// <summary>
    /// Subscribes to messages using a WCF service
    /// </summary>
    [Serializable]
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    internal sealed class Receiver : AsyncFilter
    {
        const int _port = 666;
        readonly static ILog _log = LogManager.GetLogger(typeof(Receiver));
        readonly static Uri _baseAddress = new Uri(string.Format(Uri.UriSchemeHttp + "://{0}:{1}/Klinkby.MicroBus/Receiver", Dns.GetHostEntry(Dns.GetHostName()).HostName, _port));
        //        readonly Timer _timer; // HACK
        readonly ServiceHost _host;
        readonly string _name;

       // [DebuggerStepThrough]
        protected Receiver(string name, IAsyncFilter[] children)
            : base(children)
        {
            //_timer = new Timer(
            //    (o) => ProcessAsync(new Message("abc", "baba", new byte[10]), null), // HACK
            //    null,
            //    100,
            //    1);
            
            _name = name;
            _host = CreateServiceHost(this);
            _host.Open();
        }

        static ServiceHost CreateServiceHost(Receiver service)
        {
            _log.Info("Creating ServiceHost: " + service._name + " on " + _baseAddress);
            // http://geekswithblogs.net/hinshelm/archive/2007/05/30/Creating-WCF-Service-Host-Programmatically.aspx
            var host = new ServiceHost(service, _baseAddress);

            // enable http get
            //ServiceMetadataBehavior smb;
            //EnsureBehavior(host.Description.Behaviors, out smb);
            //smb.HttpGetEnabled = true;

            // enable debugging
            //ServiceDebugBehavior sdb;
            //EnsureBehavior(host.Description.Behaviors, out sdb);
            //sdb.IncludeExceptionDetailInFaults = true;
            
            host.AddServiceEndpoint(typeof(IAsyncFilter), GetBinding(), service._name);
            //host.AddServiceEndpoint(typeof(IMetadataExchange), GetBinding(), service._name + "/mex");

            return host;
        }

        static void EnsureBehavior<T>(KeyedByTypeCollection<IServiceBehavior> behaviors, out T behavior)
            where T : IServiceBehavior, new()
        {
            if (behaviors.Contains(typeof(T)))
                behavior = (T)behaviors[typeof(T)];
            else
            {
                behavior = new T();
                behaviors.Add(behavior);
            }
        }

        static Binding GetBinding()
        {
            return new BasicHttpBinding(BasicHttpSecurityMode.None);
/*            var b = new WSDualHttpBinding(WSDualHttpSecurityMode.Message) {
//                ClientBaseAddress = _baseAddress,
                BypassProxyOnLocal = true,
            };
            b.Security.Message.ClientCredentialType = MessageCredentialType.Windows;
            b.Security.Message.NegotiateServiceCredential = true;
            return b;
            //var b = new BasicHttpBinding()
            //{
            //     UseDefaultWebProxy = true,
            //};
            return b;*/
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                _log.Debug("Closing ServiceHost");
                _host.Close();
            }
            base.Dispose(disposing);
        }

        public static IAsyncFilter FromXml(XElement e, IAsyncFilter[] children)
        {
            _log.Debug("Deserializing");
            Debug.Assert(children.Length != 0, "Receiver should have children");
            Safe.NonNull(e, "e");
            Safe.Validate(e, () => "Receiver" == e.Name.LocalName, "Not a Receiver element");
            return new Receiver(
                e.AttributeValue("name"),
                children);
        }

        public void Publish(IMessage message)
        {
            _log.Info("Received " + message.Topic);
            ProcessAsync(message, null);
        }
    }
}
